﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security;
using Windows.Storage.Streams;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.DataProtection;
using System.IO;

namespace eLuk.Security
{
	public class DataProtection
	{
		//public async Task<byte[]> Protect( string user, byte[] content )
		//{
		//	byte[] protectedContent = null;
		//	IBuffer ibProtectedContent = null;
		//	IBuffer data = CryptographicBuffer.CreateFromByteArray( content );
		//	DataReader reader1, reader2;
		//	IBuffer buff1, buff2;

		//	DataProtectionProvider Provider = new DataProtectionProvider( user );
		//	InMemoryRandomAccessStream originalData = new InMemoryRandomAccessStream();
		//	//Populate the new memory stream
		//	IOutputStream outputStream = originalData.GetOutputStreamAt( 0 );
		//	DataWriter writer = new DataWriter( outputStream );
		//	writer.WriteBuffer( data );
		//	await writer.StoreAsync();
		//	await outputStream.FlushAsync();
		//	//open new memory stream for read
		//	IInputStream source = originalData.GetInputStreamAt( 0 );

		//	//Open the output memory stream
		//	InMemoryRandomAccessStream protectedData = new InMemoryRandomAccessStream();
		//	IOutputStream dest = protectedData.GetOutputStreamAt( 0 );

		//	// Protect
		//	await Provider.ProtectStreamAsync( source, dest );
		//	//if ( await dest.FlushAsync() )
		//	await dest.FlushAsync();
		//	reader1 = new DataReader( originalData.GetInputStreamAt( 0 ) );
		//	reader2 = new DataReader( protectedData.GetInputStreamAt( 0 ) );

		//	await reader1.LoadAsync( ( uint ) originalData.Size );
		//	await reader2.LoadAsync( ( uint ) protectedData.Size );

		//	if ( originalData.Size == protectedData.Size )
		//	{
		//		buff1 = reader1.ReadBuffer( ( uint ) originalData.Size );
		//		buff2 = reader2.ReadBuffer( ( uint ) protectedData.Size );
		//		if ( CryptographicBuffer.Compare( buff1, buff2 ) )
		//		{
		//			throw new Exception( "Protection failed" );
		//		}
		//	}

		//	await protectedData.ReadAsync( ibProtectedContent, 0, InputStreamOptions.None );
		//	CryptographicBuffer.CopyToByteArray( ibProtectedContent, out protectedContent );
		//	return protectedContent;

		//}
		public async Task<MemoryStream> Protect( string descriptor, MemoryStream content )
		{
			byte[] protectedContent;
			byte[] bytes = new byte[ content.Length ];
			IBuffer data = null;
			DataReader reader1, reader2;
			IBuffer buffOriginal, buffProtected;
			DataProtectionProvider provider = null;
			InMemoryRandomAccessStream originalData = null;
			InMemoryRandomAccessStream protectedData = null;

			try
			{
				content.Read( bytes, 0, ( int ) content.Length );
				data = CryptographicBuffer.CreateFromByteArray( bytes );

				//Initiate the protection provider with given descriptor
				provider = new DataProtectionProvider( descriptor );

				//Create random access stream for original content
				originalData = new InMemoryRandomAccessStream();

				//Create random access stream for protected content
				protectedData = new InMemoryRandomAccessStream();

				//fill original data bytes into output stream
				IOutputStream outputStream = originalData.GetOutputStreamAt( 0 );
				DataWriter writer = new DataWriter( outputStream );
				writer.WriteBuffer( data );
				await writer.StoreAsync();
				await outputStream.FlushAsync();

				//open new memory stream to read the input data
				IInputStream source = originalData.GetInputStreamAt( 0 );

				//open output stream to hold the protected data
				IOutputStream dest = protectedData.GetOutputStreamAt( 0 );

				// Protect
				await provider.ProtectStreamAsync( source, dest );

				//if ( await dest.FlushAsync() )
				await dest.FlushAsync();

				reader1 = new DataReader( originalData.GetInputStreamAt( 0 ) );
				reader2 = new DataReader( protectedData.GetInputStreamAt( 0 ) );

				await reader1.LoadAsync( ( uint ) originalData.Size );
				await reader2.LoadAsync( ( uint ) protectedData.Size );
				buffOriginal = reader1.ReadBuffer( ( uint ) originalData.Size );
				buffProtected = reader2.ReadBuffer( ( uint ) protectedData.Size );

				if ( CryptographicBuffer.Compare( buffOriginal, buffProtected ) )
				{
					throw new Exception( "Protection failed" );
				}
				CryptographicBuffer.CopyToByteArray( buffProtected, out protectedContent );
				return new MemoryStream( protectedContent );
			}
			catch ( Exception ex )
			{

			}
			return null;
		}

		public async Task<Stream> UnProtect( string descriptor, IInputStream content )
		{
			byte[] unprotectedContent;
			Stream stream = content.AsStreamForRead();
			byte[] bytes = new byte[ stream.Length ];
			await stream.ReadAsync( bytes, 0, ( int ) stream.Length );
			IBuffer data = CryptographicBuffer.CreateFromByteArray( bytes );

			IBuffer buffProtected, buffUnProtected;
			
			DataProtectionProvider provider = null;
			InMemoryRandomAccessStream protectedData = null;
			InMemoryRandomAccessStream unprotectedData = null;

			DataReader reader1, reader2;

			try 
			{
				//Initiate the protection provider with given descriptor
				provider = new DataProtectionProvider( descriptor );

				//Create random access stream for protected content
				protectedData = new InMemoryRandomAccessStream();
				
				//Create random access stream for original content
				unprotectedData = new InMemoryRandomAccessStream();

				//fill original data bytes into output stream
				IOutputStream outputStream = protectedData.GetOutputStreamAt( 0 );
				DataWriter writer = new DataWriter( outputStream );
				writer.WriteBuffer( data );
				await writer.StoreAsync();
				await outputStream.FlushAsync();

				//open new memory stream to read the input data
				IInputStream source = protectedData.GetInputStreamAt( 0 );

				//open output stream to hold the protected data
				IOutputStream dest = unprotectedData.GetOutputStreamAt( 0 );

				// Protect
				await provider.UnprotectStreamAsync( source, dest );

				await dest.FlushAsync();

				reader1 = new DataReader( protectedData.GetInputStreamAt( 0 ) );
				reader2 = new DataReader( unprotectedData.GetInputStreamAt( 0 ) );

				await reader1.LoadAsync( ( uint ) protectedData.Size );
				await reader2.LoadAsync( ( uint ) unprotectedData.Size );

				buffProtected = reader1.ReadBuffer( ( uint ) protectedData.Size );
				buffUnProtected = reader2.ReadBuffer( ( uint ) unprotectedData.Size );

				if ( CryptographicBuffer.Compare( buffProtected, buffUnProtected ) )
				{
					throw new Exception( "UnProtection failed" );
				}

				CryptographicBuffer.CopyToByteArray( buffUnProtected, out unprotectedContent );

				return new MemoryStream( unprotectedContent );
			}
			catch(Exception ex)
			{
			
			}
			return stream;
		}
	
	}
}
